#!/usr/bin/env python3
"""
Full Pipeline Demonstration Script

This script demonstrates the complete C# to C++ conversion pipeline:
1. Load pre-generated Roslyn JSON (or connect to analyzer service if available)
2. Build Intermediate Representation (IR)
3. Apply normalization transformations
4. Map types from C# to C++
5. Generate C++ code (.hpp and .cpp files)
6. Write output to integration-demo/generated_cpp/

This script can work in two modes:
- Offline mode: Uses pre-generated JSON (sample_roslyn_output.json)
- Online mode: Connects to running Roslyn analyzer service

Usage:
    python run_full_pipeline.py [--online] [--analyzer-url localhost:50051]
"""

import json
import sys
from pathlib import Path
from typing import Optional
import argparse

# Add cpp-orchestrator to path
project_root = Path(__file__).parent.parent
orchestrator_path = project_root / "cpp-orchestrator" / "src"
sys.path.insert(0, str(orchestrator_path))

try:
    from orchestrator.ir.builder import build_ir_from_roslyn_json
    from orchestrator.ir.normalizer import normalize_ir
    from orchestrator.mapping.type_mapper import TypeMapper
    from orchestrator.mapping.symbol_resolver import SymbolResolver
    from orchestrator.codegen.emitter import CppEmitter
    from orchestrator.codegen.naming import NamingConvention
    print("✓ Successfully imported orchestrator modules")
except ImportError as e:
    print(f"✗ Failed to import orchestrator modules: {e}")
    print("\nPlease ensure cpp-orchestrator is installed:")
    print("  cd cpp-orchestrator && pip install -e .")
    sys.exit(1)


def load_roslyn_json(mode: str, analyzer_url: Optional[str] = None) -> dict:
    """Load Roslyn JSON output (offline or online mode)"""
    if mode == "offline":
        json_path = Path(__file__).parent / "sample_roslyn_output.json"
        print(f"\n📂 Loading pre-generated Roslyn JSON: {json_path.name}")

        if not json_path.exists():
            print(f"✗ File not found: {json_path}")
            sys.exit(1)

        with open(json_path, 'r') as f:
            data = json.load(f)

        print(f"✓ Loaded {len(data.get('classes', []))} class(es) from JSON")
        return data
    else:
        # Online mode - connect to Roslyn analyzer service
        print(f"\n🌐 Connecting to Roslyn analyzer service at {analyzer_url}")
        print("⚠ Online mode not yet fully implemented (requires running service)")
        print("  Falling back to offline mode...")
        return load_roslyn_json("offline")


def build_ir(roslyn_data: dict) -> list:
    """Build IR from Roslyn JSON data"""
    print("\n🔨 Building Intermediate Representation (IR)...")

    try:
        classes = build_ir_from_roslyn_json(roslyn_data)
        print(f"✓ Built IR for {len(classes)} class(es)")

        for cls in classes:
            print(f"  - {cls.namespace}.{cls.name}")
            print(f"    Fields: {len(cls.fields)}, Properties: {len(cls.properties)}, "
                  f"Methods: {len(cls.methods)}, Constructors: {len(cls.constructors)}")

        return classes
    except Exception as e:
        print(f"✗ Failed to build IR: {e}")
        raise


def normalize_ir_classes(classes: list) -> list:
    """Apply normalization transformations to IR"""
    print("\n🔄 Applying normalization transformations...")

    normalized_classes = []
    for cls in classes:
        try:
            normalized = normalize_ir(cls)
            normalized_classes.append(normalized)

            # Count transformations
            original_members = len(cls.fields) + len(cls.properties) + len(cls.methods)
            normalized_members = len(normalized.fields) + len(normalized.properties) + len(normalized.methods)

            print(f"  ✓ {cls.name}: {original_members} members → {normalized_members} members")

        except Exception as e:
            print(f"  ✗ Failed to normalize {cls.name}: {e}")
            raise

    return normalized_classes


def map_types(classes: list) -> list:
    """Map C# types to C++ types in IR"""
    print("\n🗺️  Mapping types from C# to C++...")

    type_mapper = TypeMapper()
    mapped_classes = []

    for cls in classes:
        try:
            # Map types in fields
            for field in cls.fields:
                mapping = type_mapper.map_type(field.field_type)
                original_type = field.field_type
                field.field_type = mapping.cpp_type
                if original_type != mapping.cpp_type:
                    print(f"  - Field {cls.name}.{field.name}: {original_type} → {mapping.cpp_type}")

            # Map types in properties
            for prop in cls.properties:
                mapping = type_mapper.map_type(prop.property_type)
                original_type = prop.property_type
                prop.property_type = mapping.cpp_type
                if original_type != mapping.cpp_type:
                    print(f"  - Property {cls.name}.{prop.name}: {original_type} → {mapping.cpp_type}")

            # Map types in methods
            for method in cls.methods:
                # Return type
                if method.return_type:
                    mapping = type_mapper.map_type(method.return_type)
                    original_type = method.return_type
                    method.return_type = mapping.cpp_type
                    if original_type != mapping.cpp_type:
                        print(f"  - Method {cls.name}.{method.name} return: {original_type} → {mapping.cpp_type}")

                # Parameter types
                for param in method.parameters:
                    mapping = type_mapper.map_type(param.param_type)
                    param.param_type = mapping.cpp_type

            # Map types in constructors
            for ctor in cls.constructors:
                for param in ctor.parameters:
                    mapping = type_mapper.map_type(param.param_type)
                    param.param_type = mapping.cpp_type

            mapped_classes.append(cls)

        except Exception as e:
            print(f"  ✗ Failed to map types for {cls.name}: {e}")
            raise

    print(f"✓ Type mapping complete for {len(mapped_classes)} class(es)")
    return mapped_classes


def generate_cpp_code(classes: list, output_dir: Path) -> None:
    """Generate C++ header and source files"""
    print(f"\n📝 Generating C++ code to {output_dir}...")

    output_dir.mkdir(parents=True, exist_ok=True)

    emitter = CppEmitter()
    symbol_resolver = SymbolResolver()
    naming = NamingConvention()

    for cls in classes:
        try:
            # Convert namespace
            cpp_namespace = symbol_resolver.convert_namespace(cls.namespace)

            # Generate header and source
            header_path, source_path = emitter.emit_class(cls, str(output_dir))

            print(f"  ✓ {cls.name}:")
            print(f"    - {Path(header_path).name}")
            print(f"    - {Path(source_path).name}")

        except Exception as e:
            print(f"  ✗ Failed to generate code for {cls.name}: {e}")
            raise

    print(f"\n✅ Generated {len(classes) * 2} C++ files successfully!")


def main():
    parser = argparse.ArgumentParser(description="Run full C# to C++ conversion pipeline")
    parser.add_argument("--online", action="store_true", help="Use online mode (connect to Roslyn service)")
    parser.add_argument("--analyzer-url", default="localhost:50051", help="Roslyn analyzer service URL")
    args = parser.parse_args()

    print("=" * 80)
    print("C# TO C++ CONVERSION PIPELINE - FULL DEMONSTRATION")
    print("=" * 80)

    mode = "online" if args.online else "offline"

    # Step 1: Load Roslyn JSON
    roslyn_data = load_roslyn_json(mode, args.analyzer_url)

    # Step 2: Build IR
    ir_classes = build_ir(roslyn_data)

    # Step 3: Normalize IR
    normalized_classes = normalize_ir_classes(ir_classes)

    # Step 4: Map Types
    mapped_classes = map_types(normalized_classes)

    # Step 5: Generate C++ Code
    output_dir = Path(__file__).parent / "generated_cpp"
    generate_cpp_code(mapped_classes, output_dir)

    print("\n" + "=" * 80)
    print("✅ PIPELINE COMPLETE!")
    print("=" * 80)
    print(f"\nGenerated files are in: {output_dir}")
    print("\nNext steps:")
    print("  1. Review generated C++ files")
    print("  2. Run verify_output.sh to validate compilation")
    print("  3. Examine the transformation process")


if __name__ == "__main__":
    main()
